package cn.anl.service.impl;

import cn.anl.constant.RedisConstant;
import cn.anl.mapper.SysMenuMapper;
import cn.anl.pojo.SysMenu;
import cn.anl.pojo.SysUser;
import cn.anl.pojo.dto.SysMenuDTO;
import cn.anl.pojo.vo.MetaVo;
import cn.anl.pojo.vo.RouterVo;
import cn.anl.service.ISysMenuService;
import cn.anl.service.RedisService;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单权限表 服务实现类
 * </p>
 *
 * @author Anl
 * @since 2024-07-06
 */
@Service
@Slf4j
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {
    @Resource
    private SysMenuMapper sysMenuMapper;
    @Resource
    private RedisService redisService;

    /**
     * @Desc 根据用户权限获取可访问的菜单ID集合
     * @Author 安奈
     * @Date 2024/7/8 9:29
     **/
    private HashSet<Long> getAccessibleMenuIds() {
        //从redis中取出用户权限
        SysUser sysUser = redisService.getCacheObject(RedisConstant.USER_INFO.getKey());
        ArrayList<String> authorities = sysUser.getAuthorities();
        //查询用户拥有该权限的角色集合ids
        ArrayList<Long> roleIds = sysMenuMapper.selectRoleIds(authorities);
        // 返回一个包含可访问菜单ID的集合
        return sysMenuMapper.selectMenuByAuth(roleIds);
    }

    private ArrayList<SysMenu> getAllMenuDirectory() {
        return sysMenuMapper.selectAllMenuDirectory(getAccessibleMenuIds());
    }

    /**
     * @Desc 查询所有菜单
     * @Author 安奈
     * @Date 2024/7/8 9:29
     * @Param: userRoles
     **/
    @Override
    public ArrayList<SysMenu> findAllMenus(ArrayList<String> like) {
        return sysMenuMapper.findAll(like);
    }


    /**
     * @Desc 搭建个人权限菜单树返回给前端
     * @Author 安奈
     * @Date 2024/7/8 17:06
     **/
    @Override
    public ArrayList<RouterVo> buildMenuTreeForUser() {
        // 使用Map来构建菜单树，key是菜单ID，value是SysMenu对象
        Map<Long, SysMenu> menuMap = findAllMenus(null).stream().collect(Collectors.toMap(SysMenu::getMenuId, Function.identity()));
        // 存放结果树
        ArrayList<RouterVo> result = new ArrayList<>();
        // 遍历查询到的所有有权限的目录菜单构建菜单树
        for (SysMenu menu : getAllMenuDirectory()) {
            RouterVo routerVo = convertToVO(menu, menuMap, getAccessibleMenuIds());
            if (routerVo != null) { // 如果用户有权访问这个菜单及其子菜单
                result.add(routerVo);
            }
        }

        return result;
    }


    /**
     * @Desc 递归构建菜单树遍历所有子菜单
     * @Author 安奈
     * @Date 2024/7/8 17:06
     * @Param: menu 菜单对象
     * @Param: menuMap map封装菜单
     * @Param: accessibleMenuIds 菜单权限Id集合
     **/
    private RouterVo convertToVO(SysMenu menu, Map<Long, SysMenu> menuMap, HashSet<Long> accessibleMenuIds) {
        if (!accessibleMenuIds.contains(menu.getMenuId())) {
            return null;
        }
        RouterVo router = new RouterVo();
        // 设置vo的属性
        router.setHidden("1".equals(menu.getVisible()));
        router.setName(menu.getRouteName());
        router.setPath(menu.getPath());
        router.setComponent(menu.getComponent());
        router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), "1".equals(menu.getIsCache().toString())));
        //递归设置子菜单
        List<RouterVo> children = new ArrayList<>();
        for (SysMenu potentialChild : menuMap.values()) {
            if (potentialChild.getParentId().equals(menu.getMenuId()) && accessibleMenuIds.contains(potentialChild.getMenuId())) {
                RouterVo childVo = convertToVO(potentialChild, menuMap, accessibleMenuIds);
                if (childVo != null) {
                    children.add(childVo);
                }
            }
        }
        // 将子菜单列表设置放在循环结束后
        router.setChildren(children);
        // 根据需要设置其他属性，如 alwaysShow, redirect 等
        if (!children.isEmpty()) {
            router.setAlwaysShow(true);
            router.setRedirect(menu.getRedirect());
        }
        return router;
    }
}